<template>
  <div>
    <slot />
    <!-- 对话框 -->
    <BasicDialog
      v-for="(dialog, ind) in dialogs"
      :key="'dialog-' + ind"
      :visible.sync="dialog.show"
      :footer="dialog.foot"
      v-bind="dialog.attrs"
    >
      <BaseRender :data="dialog.body" />
    </BasicDialog>
    <!-- 抽屉 -->
    <BasicDrawer
      v-for="(drawer, ind) in drawers"
      :key="'drawer-' + ind"
      :visible.sync="drawer.show"
      v-bind="drawer.attrs"
    >
      <BaseRender :data="drawer.body" />
    </BasicDrawer>
  </div>
</template>

<script>
import BasicDialog from './components/BasicDialog.vue';
import BasicDrawer from './components/BasicDrawer.vue';
import BaseRender from './components/BaseRender.vue';
import { sortObjArrByKey } from './utils';

let dialogTimer = null;
let drawerTimer = null;

const closeDelay = 300; // 延迟一点置为空（为了配合动画效果）

export default {
  name: 'BasePopup',
  components: {
    BasicDialog,
    BasicDrawer,
    BaseRender,
  },
  provide() {
    const { openPopup, closePopup, getPopups } = this;
    return {
      openPopup,
      closePopup,
      getPopups,
    };
  },
  data() {
    return {
      dialogs: [],
      drawers: [],
    };
  },
  methods: {
    /**
     * 构造生成新的attrs对象
     */
    getNewAttrs(head, popupId) {
      if (typeof head !== 'string') return head;
      const { closePopup } = this;
      return { title: head, onClose: () => closePopup(popupId) };
    },
    /**
     * 对话框 dialog
     */
    openDialog(head, body, foot = false) {
      const { dialogs, getNewAttrs } = this;
      if (dialogTimer) return this.$message.warning('您的操作太频繁了');
      if (typeof head === 'string' && head.startsWith('dialog-')) {
        const id = Number(head.split('-')[1]);
        const target = dialogs.find((it) => it.id === id);
        if (target) {
          target.show = true;
          return;
        }
        return this.$message.error(`不存在对话框【dialog-${id}】`);
      }
      const id = (dialogs.at(-1)?.id ?? -1) + 1;
      dialogs.push({
        id,
        name: 'dialog',
        show: true,
        attrs: getNewAttrs(head, `dialog-${id}`),
        body,
        foot,
        createAt: Date.now(),
      });
    },
    // 关闭dialog
    closeDialog(
      popup = `dialog-${this.dialogs.at(-1)?.id ?? 0}`,
      destroyed = true,
    ) {
      const { dialogs, $set } = this;
      if (popup === 'all') {
        dialogs.length = 0;
        return;
      }
      const ind = dialogs.findIndex((item) => {
        if (typeof popup !== 'string') return item === popup;
        return item.id === Number(popup.split('-')[1]);
      });
      if (ind === -1) return;
      $set(dialogs[ind], 'show', false);
      if (!destroyed) return;
      dialogTimer = setTimeout(() => {
        dialogs.splice(ind, 1);
        dialogTimer = null;
      }, closeDelay);
    },
    /**
     * 抽屉 drawer
     */
    openDrawer(head, body) {
      const { drawers, getNewAttrs } = this;
      if (drawerTimer) return this.$message.warning('您的操作太频繁了');
      if (typeof head === 'string' && head.startsWith('drawer-')) {
        const id = Number(head.split('-')[1]);
        const target = drawers.find((it) => it.id === id);
        if (target) return (target.show = true);
        return this.$message.error(`不存在抽屉【drawer-${id}】`);
      }
      const id = (drawers.at(-1)?.id ?? -1) + 1;
      drawers.push({
        id,
        name: 'drawer',
        show: true,
        attrs: getNewAttrs(head, `drawer-${id}`),
        body,
        createAt: Date.now(),
      });
    },
    // 关闭抽屉
    closeDrawer(
      popup = `drawer-${this.drawers.at(-1)?.id ?? 0}`,
      destroyed = true,
    ) {
      const { drawers, $set } = this;
      if (popup === 'all') {
        drawers.length = 0;
        return;
      }
      const ind = drawers.findIndex((item) => {
        if (typeof popup !== 'string') return item === popup;
        return item.id === Number(popup.split('-')[1]);
      });
      if (ind === -1) return;
      $set(drawers[ind], 'show', false);
      if (!destroyed) return;
      drawerTimer = setTimeout(() => {
        drawers.splice(ind, 1);
        drawerTimer = null;
      }, closeDelay);
    },
    /**
     * 混合弹出层 popup
     */
    openPopup(head, body, type = 'dialog', foot = false) {
      const { openDialog, openDrawer } = this;
      if (typeof head === 'string') {
        const isDiaId = head.startsWith('dialog-');
        if (isDiaId || head.startsWith('drawer-'))
          return isDiaId ? openDialog(head) : openDrawer(head);
      }
      if (type === 'dialog') return openDialog(head, body, foot);
      if (type === 'drawer') return openDrawer(head, body);
      // 如果不是弹窗类型，则打开dialog，且type的值作为dialog的footer渲染
      return openDialog(head, body, type);
    },
    /**
     * 获取顶层弹窗的id数组
     */
    getTopPopupIds(num = 1) {
      if (num <= 0) throw new Error('请传入一个正整数');
      const { dialogs, drawers } = this;
      const popups = sortObjArrByKey(
        [...dialogs, ...drawers],
        'createAt',
        'desc',
      );
      const topPops = popups.slice(0, num);
      return topPops?.map((it) => `${it.name}-${it.id}`);
    },
    closePopup(popup = 1, destroyed = true) {
      const { closeDialog, closeDrawer, getTopPopupIds } = this;
      if (popup === 'all') {
        closeDialog(popup);
        closeDrawer(popup);
        return;
      }
      if (typeof popup === 'string') {
        const isId = popup.includes('-');
        if (popup.startsWith('dialog')) {
          const isAll = popup === 'dialog';
          const dia = isId ? popup : isAll ? 'all' : undefined;
          return closeDialog(dia, destroyed);
        }
        if (popup.startsWith('drawer')) {
          const isAll = popup === 'drawer';
          const dra = isId ? popup : isAll ? 'all' : undefined;
          return closeDrawer(dra, destroyed);
        }
        throw new Error(`暂未处理此种弹窗类型：${popup}`);
      }
      if (typeof popup === 'number') {
        const ids = getTopPopupIds(popup);
        ids.forEach((id) => this.closePopup(id));
        return;
      }
      // 排除null、事件对象
      if (typeof popup === "object" && popup) {
        const isPopupObj =
          Object.keys(popup).length <= 7 &&
          "id" in popup &&
          "name" in popup &&
          "show" in popup &&
          "attrs" in popup &&
          "body" in popup &&
          // "foot" in popup &&
          "createAt" in popup;
        if (!isPopupObj) return this.closePopup();
        const isDialog = popup.name === "dialog";
        return isDialog ? closeDialog(popup) : closeDrawer(popup);
      }
      throw new Error(`暂未处理此类型${typeof popup}`);
    },
    /**
     * 获取弹出层栈
     */
    getPopups(type) {
      const { dialogs, drawers } = this;
      if (type === 'dialog') return JSON.parse(JSON.stringify(dialogs));
      if (type === 'drawer') return JSON.parse(JSON.stringify(drawers));
      return JSON.parse(JSON.stringify({ dialogs, drawers }));
    },
  },
};
</script>
